package copyengine.utils.tick
{
    import copyengine.utils.GeneralUtils;
    import copyengine.utils.tick.node.CountTickObjectNode;
    import copyengine.utils.tick.node.TickObjectNode;

    import flash.events.Event;
    import flash.events.TimerEvent;
    import flash.utils.Timer;
    import flash.utils.getTimer;

    /**
     * GlobalTick is convenient utils class, it's usually use in this condition
     *
     * 1) have an effect MovieClip , we only want it paly certain time . and then remove form it parent.
     *
     * 2) hava an class need to update it property , but that property maybe will be changed in the same tick.
     *     so in testCase to optimize performance , that property need to change in next frame.
     *
     * 3) need to play an effect in dialog show hide
     *
     * @author Tunied
     *
     */
    public class GlobalTick
    {
        private static var _instance:GlobalTick;

        public static function get instance():GlobalTick
        {
            if (_instance == null) {
                _instance = new GlobalTick();
            }
            return _instance;
        }

        private var holdTimer:Timer;

        private var lastTickTime:int;

        private var firstTickObjectNode:TickObjectNode; //double linked list , hold the like head

        public function GlobalTick()
        {
            lastTickTime = getTimer();
            //FPS 24 理论timer设置应该为42
            //FPS=24 --> 1000/24 ~= 42
            //但是设置42后 每次Tick时间约为86ms
            //但是设置40ms却比较准确，具说和Flash跑到模型有关
            holdTimer = new Timer(40);
            GeneralUtils.addTargetEventListener(holdTimer, TimerEvent.TIMER, tick);
            holdTimer.start();
        }

        /**
         * after intervalTick , then will call the function back. use in testCase 2 (See GlobalTick)
         *
         * @param _f                            每次回调函数（回调函数不接受参数）
         *
         * @param _tick            每次回调间隔Tick数
         *
         * @param _repeatTime            Tick重复次数
         *
         * @param _isSington                是否整个Tick队列中只能存在唯一的回调的函数
         */
        public function callLaterAfterTickCount(_f:Function, _tick:int = 1, _repeatTime:int = 0, _isSington:Boolean = true):void
        {
            if (!_isSington || !isContainFunction(_f)) {
                var tickNode:TickObjectNode = new CountTickObjectNode(_f, _tick, _repeatTime);
                addToTickQueue(tickNode);
            }
        }

        /**
         * use the tick time to simulation timer ( use in scrollBar thumb move.)
         */
        public function callLaterAfterTimerCount(_f:Function, _second:Number, _repeatTime:int = 0, _isSingleton:Boolean = true):void
        {
            if (!_isSingleton || !isContainFunction(_f)) {
                var tickNode:TickObjectNode = new TickObjectNode(_f, _second * 1000, _repeatTime);
                addToTickQueue(tickNode);
            }
        }

        private function tick(e:Event):void
        {
            var currentTime:int = getTimer();
            var elapseTime:int = currentTime - lastTickTime;
            lastTickTime = currentTime;
            var node:TickObjectNode = firstTickObjectNode;
            var nextNode:TickObjectNode;
            while (node != null) {
                nextNode = node.getNext() as TickObjectNode;
                if (node.isNeedRemove) {
                    node.destory();
                    removeFromTickQueue(node);
                }
                else {
                    node.tick(elapseTime);
                }
                node = nextNode;
            }
        }

        public function removeTickNodeByFunction(_f:Function):void
        {
            if (_f != null) {
                var node:TickObjectNode = firstTickObjectNode;
                var nextNode:TickObjectNode;
                while (node != null) {
                    nextNode = node.getNext() as TickObjectNode;
                    if (node.tickFinishedCallBackFunction == _f) {
                        node.isNeedRemove = true;
                        node.tickFinishedCallBackFunction = null;
                        break;
                    }
                    node = nextNode;
                }
            }
        }

        private function addToTickQueue(_tickNode:TickObjectNode):void
        {
            _tickNode.setNext(firstTickObjectNode);
            firstTickObjectNode = _tickNode;
        }

        private function removeFromTickQueue(_tickNode:TickObjectNode):void
        {
            if (firstTickObjectNode == _tickNode) // current Node is first node.
            {
                firstTickObjectNode = _tickNode.getNext() as TickObjectNode;
                _tickNode.setNext(null);
            }
            else {
                _tickNode.getPrevious().setNext(_tickNode.getNext());
            }
        }

        private function isContainFunction(_f:Function):Boolean
        {
            var node:TickObjectNode = firstTickObjectNode;
            var nextNode:TickObjectNode;
            while (node != null) {
                nextNode = node.getNext() as TickObjectNode;
                if (node.tickFinishedCallBackFunction == _f && !node.isNeedRemove) {
                    return true;
                }
                node = nextNode;
            }
            return false;
        }

    }
}

